<template>
  <div
    v-if="!initializing"
    class="vc-page vc-flex-column vc-grow-height"
    :style="visual.parseStyle(pageNode.currStyle)"
  >
    <Component
      v-for="(child, index) in pageNode.children"
      :key="index"
      :is="child.compName"
      :ref="el => setRefs(el, child)"
      :node="child"
    />
    <el-dialog
      v-model="dialogOption.visible"
      :title="dialogOption.title"
      :width="dialogOption.showWidth ? dialogOption.showWidth : '800px'"
      :fullscreen="dialogOption.fullscreen"
      :close-on-click-modal="dialogOption.closeOnClickModal"
      :class="['vc-dialog', 'no-padding', !dialogOption.showTitle ? 'no-header' : '']"
      destroy-on-close
      draggable
      @close="closeDialog(dialogOption)"
    >
      <template #header>
        <div class="header" @dblclick="dialogOption.fullscreen = !dialogOption.fullscreen">
          <div class="title">{{ dialogOption.title }}</div>
          <div class="button">
            <cc-icon
              v-if="!dialogOption.fullscreen"
              iconId="018dca8cad1b28075ecd"
              @click="dialogOption.fullscreen = true"
              title="最大化"
            />
            <cc-icon
              v-if="dialogOption.fullscreen"
              iconId="018dca8cad1b9b03c47f"
              @click="dialogOption.fullscreen = false"
              title="缩小"
            />
          </div>
        </div>
      </template>
      <page
        ref="dialogCompRef"
        v-bind="dialogOption.options"
        :data-page-id="dialogOption.pageId"
        :fullHeight="true"
      />
      <template #footer v-if="dialogOption.showFooter">
        <el-button
          size="default"
          type="info"
          @click="dialogOption.visible = false"
        >关闭</el-button>
        <el-button
          v-if="dialogOption.dialogEdit"
          size="default"
          type="primary"
          @click="() => submitDialogForm()"
          :loading="dialogOption.dialogLoading"
        >提交</el-button>
      </template>
    </el-dialog>
    <el-drawer
      v-model="drawerOption.visible"
      :with-header="drawerOption.showTitle"
      :title="drawerOption.title"
      :size="drawerOption.fullscreen ? '100%' : drawerOption.showWidth ? drawerOption.showWidth : '800px'"
      :fullscreen="drawerOption.fullscreen"
      :close-on-click-modal="drawerOption.closeOnClickModal"
      :class="['vc-drawer', 'no-padding', !drawerOption.showTitle ? 'no-header' : '']"
      destroy-on-close
      @close="closeDialog(drawerOption)"
    >
      <template #header>
        <div class="header" @dblclick="drawerOption.fullscreen = !drawerOption.fullscreen">
          <div class="title">{{ drawerOption.title }}</div>
          <div class="button">
            <cc-icon
              v-if="!drawerOption.fullscreen"
              iconId="018dca8cad1b28075ecd"
              @click="drawerOption.fullscreen = true"
              title="最大化"
            />
            <cc-icon
              v-if="drawerOption.fullscreen"
              iconId="018dca8cad1b9b03c47f"
              @click="drawerOption.fullscreen = false"
              title="缩小"
            />
          </div>
        </div>
      </template>
      <page
        ref="drawerCompRef"
        v-bind="drawerOption.options"
        :data-page-id="drawerOption.pageId"
        :fullHeight="true"
      />
      <template #footer v-if="drawerOption.showFooter">
        <el-button
          size="default"
          type="info"
          @click="drawerOption.visible = false"
        >关闭</el-button>
      </template>
    </el-drawer>
  </div>
  <div
    v-else
    v-loading="true"
    element-loading-text="数据加载中..."
    class="vc-page vc-flex-column vc-grow-height"
    style="min-height: 300px"
  ></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, provide, getCurrentInstance, nextTick, markRaw, inject } from 'vue';
import api from "@/http/api";
import visual from "@/visual/js/visual";
import EventHandler from "@/visual/js/event";
import page from "@/codeless/view/page.vue";
import { useVisualCompStore } from '@/visual/js/visual-comp';
import { getPage, getPageComp } from '@/utils/codeless';
import { ElMessage, ElMessageBox } from "element-plus";

const props = defineProps({
  dataPageId: String,
  pageInfo: Object,
  visualJson: Object,
  operate: {
    type: String,
    default: '',
  },
  inputParam: {
    type: Object,
    default: () => {}
  },
  globalParam: {
    type: Object,
    default: () => {}
  }
});

const initializing = ref(true);
const pageCompRefs = ref({});
const pageInfo = ref({});
const pageNode = ref({});
const currMenuId = inject('menuId', '');
const dialogCompRef = ref();
const dialogOption = ref({
  visible: false,
  pageId: '',
  title: '',
  showWidth: '800px',
  fullscreen: false,
  dialogEdit: false,
  closeOnClickModal: false,
  showFooter: true,
  options: {}
});
const drawerCompRef = ref();
const drawerOption = ref({
  visible: false,
  pageId: '',
  title: '',
  showWidth: '800px',
  fullscreen: false,
  closeOnClickModal: false,
  showFooter: true,
  options: {}
});
const visualComp = useVisualCompStore();
const eventHandler = new EventHandler(null, getCurrentInstance(), pageNode.value);
const globalParam = Object.assign({}, props.globalParam, props.inputParam);
eventHandler.setGlobalParam(globalParam);

let pageData = inject('pageData', null);
const pageInputParam = ref(Object.assign({}, props.inputParam, props.globalParam));
if (pageData) {
  const currPageData = ref({"inputParam": pageInputParam});
  pageData.value[props.pageInfo?.name || '页面'] = currPageData;
  provide('pageData', currPageData);
  pageData = currPageData;
} else {
  pageData = ref({"inputParam": pageInputParam});
  provide('pageData', pageData);
}
provide('operate', props.operate);
provide('eventHandler', eventHandler);

const setRefs = (el, comp) => {
  if (el) {
    pageCompRefs.value[comp.id] = el;
  }
};

const loadPageInfo = async() => {
  if (props.visualJson) {
    pageNode.value = props.visualJson;
  } else {
    if (props.pageInfo) {
      pageInfo.value = props.pageInfo;
    } else {
      pageInfo.value = await getPage(props.dataPageId());
    }

    if (pageInfo.value.compList
        && pageInfo.value.compList.length > 0
        && pageInfo.value.compList[0].id) {
      const pageComp = await getPageComp(pageInfo.value.compList[0].id);
      pageNode.value = JSON.parse(pageComp.visualJson);
    }
  }

  if (!pageNode.value.currStyle
      && pageNode.value.styleList
      && pageNode.value.styleList.length > 0) {
    pageNode.value.currStyle = pageNode.value.styleList[0].style || {};
  }
  eventHandler.setNode(pageNode.value);
  eventHandler.initInputParam();

  for (const comp of pageNode.value.children) {
    const res = await visualComp.getVisualComp(comp.type);
    comp.compName = markRaw(res.name);
  }

  const dataList = (await visual.loadData(pageNode.value.compData, eventHandler, getData(), pageNode.value)).data || [];
  const data = dataList.length > 0 ? dataList[0] : {};

  (pageNode.value.compData.fieldList || []).forEach(field => {
    pageInputParam.value[field.id] = data[field.name] || '';
  });
  pageData.value['inputParam'] = Object.assign({}, pageInputParam.value);
  eventHandler.setGlobalParam(pageInputParam.value);
};

const init = async() => {
  if (!props.dataPageId && !props.pageInfo && !props.visualJson) {
    return;
  }

  initializing.value = true;
  await loadPageInfo();
  await nextTick(async() => {
    initializing.value = false;
  });
};

const validate = (callback) => {
  const promise = new Promise((resolve, reject) => {
    let keys = Object.keys(pageCompRefs.value);

    if (keys.length === 0) {
      resolve(true);
      return;
    }

    const validates = keys.map(key => {
      const component = pageCompRefs.value[key];
      if (!component.validate) {
        return Promise.resolve(true);
      }

      return new Promise((innerResolve, innerReject) => {
        component.validate(valid => {
          if (!valid) {
            innerReject();
          } else {
            innerResolve();
          }
        }).catch(innerReject);
      });
    });

    Promise.all(validates)
      .then(() => {
        resolve(true);
      })
      .catch(reject);
  });

  promise.then(valid => callback(valid));
  return promise;
};

const getData = () => {
  return Object.assign({}, pageData.value, {inputParam: pageNode.value.inputParam || {}});
};

const getCurrNode = () => {
  return pageNode.value;
}

const showDialog = (options) => {
  Object.assign(dialogOption.value, options);
  dialogOption.value.visible = true;
  eventHandler.addDialog(dialogOption.value);
};

const showDrawer = (options) => {
  Object.assign(drawerOption.value, options);
  drawerOption.value.visible = true;
  eventHandler.addDialog(dialogOption.value);
};

const closeDialog = (option) => {
  eventHandler.closeDialog(option);
};

const refresh = () => {
  initializing.value = true;
  nextTick(() => initializing.value = false);
};

const submitDialogForm = () => {
  let prompt = "请确认是否提交数据？";
  ElMessageBox.confirm(prompt, '提示', {type: 'info'}).then(() => {
    let url = '/codeless/'
      + currMenuId.value + '/visual/'
      + dialogOption.value.pageId + '/savePage';

    dialogOption.value.dialogLoading = true;
    api.post(url, dialogCompRef.value.getData()).then(res => {
      ElMessage.success(res.message);
      dialogOption.value.dialogLoading = false;
      dialogOption.value.visible = false;
      refresh();
    }).catch(e => {
      dialogOption.value.dialogLoading = false;
      console.log('操作失败！', e);
    });
  }).catch(() => {});
};

onMounted(async() => {
  await init();
});

onUnmounted(() => {
  eventHandler.destroy();
});

defineExpose({ getData, validate, getCurrNode, showDialog, showDrawer });
</script>

<style scoped>
.vc-page {
  --el-loading-spinner-size: 25px;
}
</style>